package newsserver;

import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

@WebService(serviceName = "NewsServer")
public class NewsServer {

    DocumentBuilder docBuild;
    TransformerFactory transFac;

    public NewsServer() throws ParserConfigurationException {
        DocumentBuilderFactory docBuildFac = DocumentBuilderFactory.newInstance();
        docBuild = docBuildFac.newDocumentBuilder();
        transFac = TransformerFactory.newInstance();
    }

    // input example: <count>3</count>
    // output example: <list><id>5</id><id>9</id><id>10</id></list>

    @WebMethod(operationName = "listArticles")
    public String listArticles(@WebParam(name = "count") String count) {
        try {
            // parse request
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(count));
            Document doc = docBuild.parse(is);

            Integer c = null;
            c = Integer.parseInt(((CharacterData) doc.
                    getElementsByTagName("count").item(0).
                    getFirstChild()).getData());

            // assertions
            if (c == null || c < 0) {
                return "ERROR: listArticles is " + c + ", must be 0 or greater";
            }

            // send request to database
            doc = docBuild.newDocument();
            Element root = doc.createElement("count");
            doc.appendChild(root);
            root.appendChild(doc.createTextNode(""+c));
            String request = xmlToString(doc);
            String response = DatabaseClient.listArticles(request);

            // parse response
            is = new InputSource();
            is.setCharacterStream(new StringReader(response));
            doc = docBuild.parse(is);
            NodeList idNodes = doc.getElementsByTagName("id");
            NodeList titleNodes = doc.getElementsByTagName("title");
            List<Integer> idList = new LinkedList<Integer>();
            List<String> titleList = new LinkedList<String>();
            for (int i=0; i<idNodes.getLength(); i++) {
                idList.add(Integer.parseInt(((CharacterData) idNodes.item(i).getFirstChild()).getData()));
                titleList.add(((CharacterData) titleNodes.item(i).getFirstChild()).getData());
            }

            // respond to client
            doc = docBuild.newDocument();
            root = doc.createElement("list");
            doc.appendChild(root);
            for (int i=0; i<idList.size(); i++) {
                Element art = doc.createElement("article");
                root.appendChild(art);
                Element e = doc.createElement("id");
                art.appendChild(e);
                e.appendChild(doc.createTextNode(idList.get(i).toString()));
                e = doc.createElement("title");
                art.appendChild(e);
                e.appendChild(doc.createTextNode(titleList.get(i)));
            }

            return xmlToString(doc);
        } catch (Throwable t) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            return "ERROR: " + t.getMessage() + "\n" + sw.toString();
        }
    }

    // input example: <id>2</id>
    // output example: <article><id>...</id><title>...</title><content>...</content><category>...</category><author>...</author><time>...</time></article>

    @WebMethod(operationName = "getArticle")
    public String getArticle(@WebParam(name = "id") String strID) {
        try {
            // parse request
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(strID));
            Document doc = docBuild.parse(is);

            Integer id = null;
            id = Integer.parseInt(((CharacterData) doc.
                    getElementsByTagName("id").item(0).
                    getFirstChild()).getData());

            // assertions
            if (id == null) {
                return "ERROR: ID is null";
            }

            // send request to database
            doc = docBuild.newDocument();
            Element root = doc.createElement("id");
            doc.appendChild(root);
            root.appendChild(doc.createTextNode(""+id));
            String request = xmlToString(doc);
            String response = DatabaseClient.getArticle(request);

            // parse response
            is = new InputSource();
            is.setCharacterStream(new StringReader(response));
            doc = docBuild.parse(is);
            Map<String,String> set = new HashMap<String,String>();
            NodeList children = doc.getElementsByTagName("article").item(0).getChildNodes();
            for (int i=0; i<children.getLength(); i++) {
                Node child = children.item(i);
                String childName = child.getNodeName();
                String childData = ((CharacterData) (child.getFirstChild())).getData();
                set.put(childName, childData);
            }

            // ask the database what the name of the author is
            doc = docBuild.newDocument();
            root = doc.createElement("id");
            doc.appendChild(root);
            root.appendChild(doc.createTextNode(set.get("authorID")));
            String authorRequest = xmlToString(doc);
            String authorResponse = DatabaseClient.getAuthor(authorRequest);

            // parse response
            is = new InputSource();
            is.setCharacterStream(new StringReader(authorResponse));
            doc = docBuild.parse(is);
            set.put("author", doc.getElementsByTagName("name").item(0).getTextContent());
            set.remove("authorID");

            // respond to client
            doc = docBuild.newDocument();
            root = doc.createElement("article");
            doc.appendChild(root);
            for (String key : set.keySet()) {
                Element e = doc.createElement(key);
                root.appendChild(e);
                e.appendChild(doc.createTextNode(set.get(key)));
            }

            return xmlToString(doc);
        } catch (Throwable t) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            return "ERROR: " + t.getMessage() + "\n" + sw.toString();
        }
    }
    
    // input example: <category>Symaskiner</category>
    // output example: <list><id>1</id><id>2</id></list>
    
    @WebMethod(operationName = "browseCategory")
    public String browseCategory(@WebParam(name = "category") String category) {
        try {
            // parse request
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(category));
            Document doc = docBuild.parse(is);

            String cat = ((CharacterData) doc.
                    getElementsByTagName("category").item(0).
                    getFirstChild()).getData();

            // assertions
            if (cat == null) {
                return "ERROR: category is null";
            }

            // send request to database
            doc = docBuild.newDocument();
            Element root = doc.createElement("category");
            doc.appendChild(root);
            root.appendChild(doc.createTextNode(""+cat));
            String request = xmlToString(doc);
            String response = DatabaseClient.browseCategory(request);

            // parse response
            is = new InputSource();
            is.setCharacterStream(new StringReader(response));
            doc = docBuild.parse(is);
            NodeList idNodes = doc.getElementsByTagName("id");
            NodeList titleNodes = doc.getElementsByTagName("title");
            List<Integer> idList = new LinkedList<Integer>();
            List<String> titleList = new LinkedList<String>();
            for (int i=0; i<idNodes.getLength(); i++) {
                idList.add(Integer.parseInt(((CharacterData) idNodes.item(i).getFirstChild()).getData()));
                titleList.add(((CharacterData) titleNodes.item(i).getFirstChild()).getData());
            }

            // respond to client
            doc = docBuild.newDocument();
            root = doc.createElement("list");
            doc.appendChild(root);
            for (int i=0; i<idList.size(); i++) {
                Element art = doc.createElement("article");
                root.appendChild(art);
                Element e = doc.createElement("id");
                art.appendChild(e);
                e.appendChild(doc.createTextNode(idList.get(i).toString()));
                e = doc.createElement("title");
                art.appendChild(e);
                e.appendChild(doc.createTextNode(titleList.get(i)));
            }

            return xmlToString(doc);
        } catch (Throwable t) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            return "ERROR: " + t.getMessage() + "\n" + sw.toString();
        }
    }
    
    @WebMethod(operationName = "listCategories")
    public String listCategories() {
        try {
            // send request to database
            String response = DatabaseClient.listCategories();

            // parse response
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(response));
            Document doc = docBuild.parse(is);
            NodeList nodes = doc.getElementsByTagName("name");
            List<String> list = new LinkedList<String>();
            for (int i=0; i<nodes.getLength(); i++) {
                list.add(((CharacterData) nodes.item(i).getFirstChild()).getData());
            }

            // respond to client
            doc = docBuild.newDocument();
            Element root = doc.createElement("list");
            doc.appendChild(root);
            for (String i : list) {
                Element e = doc.createElement("name");
                root.appendChild(e);
                e.appendChild(doc.createTextNode(i));
            }

            return xmlToString(doc);
        } catch (Throwable t) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            return "ERROR: " + t.getMessage() + "\n" + sw.toString();
        }
    }

    private String xmlToString(Document doc) throws Exception {
        StringWriter buffer = new StringWriter();
        Transformer transformer = transFac.newTransformer();
        transformer.transform(new DOMSource(doc), new StreamResult(buffer));
        return buffer.toString();
    }
}
